package github.ishaan.buttonprogressbar;


import github.ishaan.buttonprogressbar.utils.AttrValue;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.element.Element;
import ohos.agp.components.element.ElementScatter;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.utils.Color;
import ohos.agp.utils.RectFloat;
import ohos.agp.window.service.DisplayManager;
import ohos.app.Context;

/**
 * Created by ishaan on 13/3/17.
 */

public class ButtonProgressBar extends Component implements Component.EstimateSizeListener, Component.DrawTask {

    private Paint mTextPaint;
    private Paint mBackgroundPaint;
    private Paint mProgressPaint;
    private String mInitialText = "DOWNLOAD";
    private static final int DEFAUTLT_TEXTSIZE = 54;
    private int mTextSize;
    private Color mTextColor;
    private Color mBackgroundColor;
    private Color mProgressColor;
    private int mCornerRadius = 10;
    private Element mIdleIcon;
    private int indeterminateState = -1;
    private final int topX = 0;
    private final int topY = 0;
    private final int MIN = 0;
    private final int MAX = 100;
    private float mProgress = MIN;
    private Type mLoaderType = Type.DETERMINATE;
    private int mProgressIncrementFactor = 2;
    private int mCounterFactor = 0;
    private int mMaxCounterFactor = INDETERMINATE_ANIMATION_VALUES.length - 1;
    private final static int STATE_RESET = -1;
    private final static int STATE_START = 1;
    private final static int STATE_STOP = 0;
    private static final Color DEFAULT_BGCOLOR = new Color(Color.getIntColor("#0E8DD4")),
            DEFAULT_PROGCOLOR = new Color(Color.getIntColor("#0399E5"));
    private int mIdleIconWidth;
    private int mIdleIconHeight;

    /**
     * 构造函数
     *
     * @param context context
     */
    public ButtonProgressBar(Context context) {
        super(context);
    }

    /**
     * 构造函数
     *
     * @param context context
     * @param attrSet attrSet
     */
    public ButtonProgressBar(Context context, AttrSet attrSet) {
        super(context, attrSet);
        init(context, attrSet);
    }

    /**
     * 构造函数
     *
     * @param context context
     * @param attrSet attrSet
     * @param styleName styleName
     */
    public ButtonProgressBar(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        init(context, attrSet);
    }

    /**
     * 构造函数
     *
     * @param context context
     * @param attrSet attrSet
     * @param resId resId
     */
    public ButtonProgressBar(Context context, AttrSet attrSet, int resId) {
        super(context, attrSet, resId);
        init(context, attrSet);
    }


    /**
     * Initialize view params to user values if attrs != null
     * else provide default values.
     *
     * @param context - view context
     * @param attrs   - attribute set provided by user in layout xml file
     */
    public void init(Context context, AttrSet attrs) {
        mBackgroundColor = AttrValue.get(attrs, "bgColor", DEFAULT_BGCOLOR);
        mProgressColor = AttrValue.get(attrs, "progColor", DEFAULT_PROGCOLOR);
        mTextColor = AttrValue.get(attrs, "textColor", Color.WHITE);
        mTextSize = AttrValue.getDimension(attrs, "textSize", DEFAUTLT_TEXTSIZE);
        int type = AttrValue.get(attrs, "type", 0);
        mInitialText = AttrValue.get(attrs, "text", "");
        switch (type) {
            case 0:
                mLoaderType = Type.DETERMINATE;
                break;
            case 1:
                mLoaderType = Type.INDETERMINATE;
                break;
        }
        backgroundPaint();
        progressPaint();
        textPaint();
        int mDefaultSize = vpToPx(context, 24);
        mIdleIcon = ElementScatter.getInstance(context).parse(ResourceTable.Graphic_ic_done);
        mIdleIconWidth = mIdleIcon.getMinWidth();
        mIdleIconHeight = mIdleIcon.getMinHeight();

        if (mIdleIconWidth == 0 || mIdleIconHeight == 0) {
            mIdleIconWidth = mDefaultSize;
            mIdleIconHeight = mDefaultSize;
        }
        setEstimateSizeListener(this);
        addDrawTask(this);
    }

    /**
     * 初始化背景paint
     */
    public void backgroundPaint() {
        mBackgroundPaint = new Paint();
        mBackgroundPaint.setAntiAlias(true);
        mBackgroundPaint.setColor(mBackgroundColor);
        mBackgroundPaint.setStyle(Paint.Style.FILLANDSTROKE_STYLE);
    }

    /**
     * 初始化进度条paint
     */
    public void progressPaint() {
        mProgressPaint = new Paint();
        mBackgroundPaint.setAntiAlias(true);
        mProgressPaint.setColor(mProgressColor);
        mProgressPaint.setStyle(Paint.Style.FILLANDSTROKE_STYLE);
    }

    /**
     * 初始化文字paint
     */
    public void textPaint() {
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(mTextColor);
        mTextPaint.setTextSize(mTextSize);
        mTextPaint.setStyle(Paint.Style.FILLANDSTROKE_STYLE);
    }

    /**
     * 背景颜色
     *
     * @param bgColor bgColor
     */
    public void setBackgroundColor(Color bgColor) {
        this.mBackgroundColor = bgColor;
        mBackgroundPaint.setColor(bgColor);
        invalidate();
    }

    /**
     * 进度条颜色
     *
     * @param progColor progColor
     */
    public void setProgressColor(Color progColor) {
        this.mProgressColor = progColor;
        mProgressPaint.setColor(progColor);
        invalidate();
    }

    /**
     * 文字颜色
     *
     * @param textColor textColor
     */
    public void setTextColor(Color textColor) {
        this.mTextColor = textColor;
        mTextPaint.setColor(textColor);
        invalidate();
    }

    /**
     * 文字大小
     *
     * @param size size
     */
    public void setTextSize(int size) {
        mTextSize = size;
        mTextPaint.setTextSize(size);
        invalidate();
    }


    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        int height, width;
        int specWidth = EstimateSpec.getSize(widthMeasureSpec);
        int specHeight = EstimateSpec.getSize(heightMeasureSpec);

        int heightMode = EstimateSpec.getMode(heightMeasureSpec);
        int widthMode = EstimateSpec.getMode(widthMeasureSpec);

        switch (heightMode) {
            case EstimateSpec.PRECISE:
                height = specHeight;
                break;
            default:
            case EstimateSpec.UNCONSTRAINT:
            case EstimateSpec.NOT_EXCEED:
                height = vpToPx(getContext(), DEFAUTLT_TEXTSIZE);
                break;
        }

        switch (widthMode) {
            case EstimateSpec.PRECISE:
                width = specWidth;
                break;
            default:
            case EstimateSpec.UNCONSTRAINT:
            case EstimateSpec.NOT_EXCEED:
                width = vpToPx(getContext(), DEFAUTLT_TEXTSIZE);
                break;
        }
        setEstimatedSize(width, height);
        return false;
    }

    /**
     * Set Loader type to determinate or indeterminate
     *
     * @param mLoaderType mLoaderType
     */
    public void setLoaderType(Type mLoaderType) {
        this.mLoaderType = mLoaderType;
    }

    /**
     * Get Loader type to determinate or indeterminate
     *
     * @return mLoaderType mLoaderType
     */
    public Type getLoaderType() {
        return this.mLoaderType;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        if (mLoaderType == Type.DETERMINATE) {
            drawDeterminateProgress(canvas);
        } else {
            drawIndeterminateProgress(canvas);
        }
    }

    private void drawIndeterminateProgress(Canvas canvas) {
        switch (indeterminateState) {
            case STATE_RESET:
                onDrawInit(canvas);
                break;
            case STATE_START:
                updateProgress();
                onDrawProgress(canvas);
                invalidate();
                break;
            case STATE_STOP:
                onDrawFinished(mIdleIcon, canvas, mIdleIconWidth, mIdleIconHeight);
                mProgress = 0;
                mProgressIncrementFactor = 2;
                mCounterFactor = 0;
                break;
        }
    }


    /**
     * Update mProgress value based on static pre-calculated value and incrementFactor
     */
    public void updateProgress() {
        if (mProgressIncrementFactor == 4) {
            mProgressIncrementFactor = 1;
        }
        if (mCounterFactor >= mMaxCounterFactor) {
            mCounterFactor = 1;
        }
        mProgress = INDETERMINATE_ANIMATION_VALUES[mCounterFactor] * MAX;
        if (mProgress <= 1) {
            mProgress = MIN;
        }
        mCounterFactor += mProgressIncrementFactor;
        mProgressIncrementFactor++;
    }

    private void drawDeterminateProgress(Canvas canvas) {
        if (mProgress == MIN) {
            onDrawInit(canvas);
        } else if (mProgress > MIN && mProgress < MAX) {
            onDrawProgress(canvas);
        } else {
            onDrawFinished(mIdleIcon, canvas, mIdleIconWidth, mIdleIconHeight);
        }
    }

    /**
     * draw initial state of mProgress view when view is either created or in reset state.
     *
     * @param canvas - view canvas object
     */
    public void onDrawInit(Canvas canvas) {
        RectFloat bgRectf = new RectFloat(topX, topY, getWidth(), getHeight());
        canvas.drawRoundRect(bgRectf, mCornerRadius, mCornerRadius, mBackgroundPaint);
        float middle = mTextPaint.measureText(mInitialText);
        int xPos = (int) ((getWidth() - middle) / 2);
        int textAdjust = (int) (mTextPaint.descent() + mTextPaint.ascent()) / 2;
        int yPos = ((getHeight() / 2) - textAdjust);
        canvas.drawText(mTextPaint, mInitialText, xPos, yPos);
    }

    /**
     * Draw mProgress on view based on mLoaderType.
     *
     * @param canvas - view canvas object
     */
    public void onDrawProgress(Canvas canvas) {
        RectFloat bgRectf = new RectFloat(topX, topY, getWidth(), getHeight());
        canvas.drawRoundRect(bgRectf, mCornerRadius, mCornerRadius, mBackgroundPaint);
        float progressPoint = (((float) getWidth() / MAX) * mProgress);
        RectFloat progRect = new RectFloat(topX, topY, progressPoint, getHeight());
        canvas.drawRoundRect(progRect, mCornerRadius, mCornerRadius, mProgressPaint);
    }

    /**
     * Draw final state of mProgress view when mProgress is completed.
     *
     * @param drawable drawable
     * @param canvas canvas
     * @param width width
     * @param height height
     */
    public void onDrawFinished(Element drawable, Canvas canvas, int width, int height) {
        RectFloat bgRectf = new RectFloat(topX, topY, getWidth(), getHeight());
        canvas.drawRoundRect(bgRectf, mCornerRadius, mCornerRadius, mBackgroundPaint);
        RectFloat progRect = new RectFloat(topX, topY, getWidth(), getHeight());
        canvas.drawRoundRect(progRect, mCornerRadius, mCornerRadius, mProgressPaint);

        int left = (getWidth() / 2) - (width / 2);
        int top = (getHeight() / 2) - (height / 2);
        drawable.setBounds(left, top, left + width, top + height);
        drawable.drawToCanvas(canvas);
    }

    /**
     * if view.Type == DETERMINATE then set mProgress to argument and invalidate() view
     *
     * @param currentProgress - int mLoaderType mProgress indeterminateState varying between mMinProgress and mMaxProgress.
     */
    public void setProgress(int currentProgress) {
        if (mLoaderType == Type.DETERMINATE) {
            mProgress = currentProgress;
            invalidate();
        }
    }

    /**
     * set indeterminateState to 1 then invalidate() view
     */
    public void startLoader() {
        indeterminateState = STATE_START;
        invalidate();
    }

    /**
     * set indeterminateState to 0 then invalidate() view
     */
    public void stopLoader() {
        indeterminateState = STATE_STOP;
        invalidate();
    }

    /**
     * Reset State back to initialState
     * if view.Type == INDETERMINATE then set indeterminateState to -1
     * else set currentProgress to 0
     * finally invalidate() view
     */
    public void reset() {
        if (mLoaderType == Type.INDETERMINATE) {
            indeterminateState = STATE_RESET;
        }
        mProgress = MIN;
        invalidate();
    }


    /**
     * Enum for loader mLoaderType { determinate or indeterminate }
     */
    public enum Type {
        DETERMINATE,
        INDETERMINATE
    }

    private int vpToPx(Context context, int vp) {
        return (int) DisplayManager.getInstance().getDefaultDisplay(context).get().getAttributes().densityPixels * vp;
    }

    /**
     * Pre-calculated values [0.0000f - 1.0000f]
     * Used from FastOutSlowInInterpolator, array of values to provide smooth indeterminate loading animation
     */
    private static final float[] INDETERMINATE_ANIMATION_VALUES = new float[]{
            0.0000f, 0.0001f, 0.0002f, 0.0005f, 0.0009f, 0.0014f, 0.0020f,
            0.0027f, 0.0036f, 0.0046f, 0.0058f, 0.0071f, 0.0085f, 0.0101f,
            0.0118f, 0.0137f, 0.0158f, 0.0180f, 0.0205f, 0.0231f, 0.0259f,
            0.0289f, 0.0321f, 0.0355f, 0.0391f, 0.0430f, 0.0471f, 0.0514f,
            0.0560f, 0.0608f, 0.0660f, 0.0714f, 0.0771f, 0.0830f, 0.0893f,
            0.0959f, 0.1029f, 0.1101f, 0.1177f, 0.1257f, 0.1339f, 0.1426f,
            0.1516f, 0.1610f, 0.1707f, 0.1808f, 0.1913f, 0.2021f, 0.2133f,
            0.2248f, 0.2366f, 0.2487f, 0.2611f, 0.2738f, 0.2867f, 0.2998f,
            0.3131f, 0.3265f, 0.3400f, 0.3536f, 0.3673f, 0.3810f, 0.3946f,
            0.4082f, 0.4217f, 0.4352f, 0.4485f, 0.4616f, 0.4746f, 0.4874f,
            0.5000f, 0.5124f, 0.5246f, 0.5365f, 0.5482f, 0.5597f, 0.5710f,
            0.5820f, 0.5928f, 0.6033f, 0.6136f, 0.6237f, 0.6335f, 0.6431f,
            0.6525f, 0.6616f, 0.6706f, 0.6793f, 0.6878f, 0.6961f, 0.7043f,
            0.7122f, 0.7199f, 0.7275f, 0.7349f, 0.7421f, 0.7491f, 0.7559f,
            0.7626f, 0.7692f, 0.7756f, 0.7818f, 0.7879f, 0.7938f, 0.7996f,
            0.8053f, 0.8108f, 0.8162f, 0.8215f, 0.8266f, 0.8317f, 0.8366f,
            0.8414f, 0.8461f, 0.8507f, 0.8551f, 0.8595f, 0.8638f, 0.8679f,
            0.8720f, 0.8760f, 0.8798f, 0.8836f, 0.8873f, 0.8909f, 0.8945f,
            0.8979f, 0.9013f, 0.9046f, 0.9078f, 0.9109f, 0.9139f, 0.9169f,
            0.9198f, 0.9227f, 0.9254f, 0.9281f, 0.9307f, 0.9333f, 0.9358f,
            0.9382f, 0.9406f, 0.9429f, 0.9452f, 0.9474f, 0.9495f, 0.9516f,
            0.9536f, 0.9556f, 0.9575f, 0.9594f, 0.9612f, 0.9629f, 0.9646f,
            0.9663f, 0.9679f, 0.9695f, 0.9710f, 0.9725f, 0.9739f, 0.9753f,
            0.9766f, 0.9779f, 0.9791f, 0.9803f, 0.9815f, 0.9826f, 0.9837f,
            0.9848f, 0.9858f, 0.9867f, 0.9877f, 0.9885f, 0.9894f, 0.9902f,
            0.9910f, 0.9917f, 0.9924f, 0.9931f, 0.9937f, 0.9944f, 0.9949f,
            0.9955f, 0.9960f, 0.9964f, 0.9969f, 0.9973f, 0.9977f, 0.9980f,
            0.9984f, 0.9986f, 0.9989f, 0.9991f, 0.9993f, 0.9995f, 0.9997f,
            0.9998f, 0.9999f, 0.9999f, 1.0000f, 1.0000f
    };
}
